#include "GameState.hpp"

using namespace Ogre;

//=============================
// CONSTRUCTOR
//=============================
GameState::GameState()
{
	m_pPhysWorld = 0;
	m_pPlayerInput = 0;
	m_bQuit = false;
}

//=============================
// START
//=============================

// Basic setup of the scene and state.
void GameState::enter()
{
	// Log the action of changing states.
	OgreFramework::getSingletonPtr()->m_pLog->logMessage("Entering GameState...");
 
	// Create scene manager.
    m_pSceneMgr = OgreFramework::getSingletonPtr()->m_pRoot->createSceneManager(ST_GENERIC, "GameSceneMgr");
    m_pSceneMgr->setAmbientLight(Ogre::ColourValue(0.25f, 0.25f, 0.25f));
	m_pSceneMgr->setSkyBox(true, "GenericSpaceBox");

	// Setup the camera.
    m_pCamera = m_pSceneMgr->createCamera("GameStateCamera");
    m_pCamera->setPosition(Vector3(30, 0, 75));
	m_pCamera->lookAt(Vector3(0, 0, 0));
    m_pCamera->setNearClipDistance(1);

	// Set camera aspect ratio.
    m_pCamera->setAspectRatio(Real(OgreFramework::getSingletonPtr()->m_pViewport->getActualWidth()) /
        Real(OgreFramework::getSingletonPtr()->m_pViewport->getActualHeight()));

	// Create the Newton physics world.
	m_pPhysWorld = new OgreNewt::World();
	m_pPhysWorld->setWorldSize(Ogre::Vector3(-10000, -10000, -10000), Ogre::Vector3(10000, 10000, 10000));

	// Set our camera to the viewport.
	OgreFramework::getSingletonPtr()->m_pViewport->setCamera(m_pCamera);

	// Create the player input
	m_pPlayerInput = new ShipInput();

	// Create the ship manager used to keep track of and update all ships.
	m_ShipManager.createShipManager(m_pSceneMgr, m_pPhysWorld);

	// Parse the ship file to fill out the ship templates. For now this is
	// hardcoded, but in the future an overarching parser will take care of this.
	m_ShipManager.parseShipFile("..\\Data\\Ships\\shiparch.lua");

	// Create the Scene.
	createScene();
}

// Fills the scene with the generic required stuff.
void GameState::createScene()
{
	// Create a light.
	Ogre::Light* pSceneLight = m_pSceneMgr->createLight();
	pSceneLight->setType(Ogre::Light::LightTypes::LT_DIRECTIONAL);
	pSceneLight->setDirection(-0.5, -0.5, -1);
	//pSceneLight->setDiffuseColour(Ogre::ColourValue(1.0f, 0.921f, 0.800f));

	// Instantiate the DotSceneLoader.
	DotSceneLoader* pDotSceneLoader = new DotSceneLoader();

	// Load the DotScene into our Scene.
	pDotSceneLoader->parseDotScene("GenericSpace.scene", "General", m_pSceneMgr, m_pSceneMgr->getRootSceneNode());
	delete pDotSceneLoader;

	/******************************
	// HARDCODED STATION COLLISION
	******************************/

	// Just like Ogre, Newton uses two objects per "thing." In Newton's case,
	// it is a Collision and a Body. The Collision is analogous to the Entity,
	// and the Body is analogous to the SceneNode. You create Collisions using
	// the entity, and SceneNodes are attached to Bodies. Also just like 
	// SceneNodes, bodies must have an associated collision in order for them 
	// to do anything.

	// Get the node and entity of the station for collision setup.
	Ogre::Entity* pStationEnt = m_pSceneMgr->getEntity("WestPoint#0");
	Ogre::SceneNode* pStationNode = m_pSceneMgr->getSceneNode("WestPoint#0");

	// Create the Collision object.
	Ogre::Vector3 StationSize = pStationEnt->getBoundingBox().getSize();
	//OgreNewt::CollisionPrimitives::Box* pStationColl = new OgreNewt::CollisionPrimitives::Box(m_pPhysWorld, StationSize, 0);	
	OgreNewt::CollisionPtr CollisionPtr(new OgreNewt::CollisionPrimitives::TreeCollision(m_pPhysWorld, pStationEnt, true, 0));

	// Create the Body object using the Collision object created above.
	OgreNewt::Body* pStationBody = new OgreNewt::Body(m_pPhysWorld, CollisionPtr);
	pStationBody->setPositionOrientation(pStationNode->getPosition(), pStationNode->getOrientation());

	// Associate the Station SceneNode with this Body so that Newton will update
	// the position of this body automatically.
	pStationBody->attachNode(pStationNode);

	/******************************
	// HARDCODED STATION COLLISION
	******************************/

	// Create some ships.
	m_ShipManager.spawnNewShip("Fighter", m_pPlayerInput, "PlayerShip");

	for (int i=1; i <= 1; i++)
		m_ShipManager.spawnNewShip("Fighter", 0, "NoName", Ogre::Vector3(i*75.0f, 0.0f, i*-75.0f));

	// Create the OrbitCamera.
	ShipObject* pPlayerShip = m_ShipManager.getShipPointerByName("PlayerShip");
	m_OrbitCamera.createOrbitCamera(m_pSceneMgr, pPlayerShip->getShipSceneNode(), m_pCamera, OgreFramework::getSingletonPtr()->m_pMouse);
	//m_OrbitCamera.createOrbitCamera(m_pSceneMgr, m_pSceneMgr->getRootSceneNode(), m_pCamera, OgreFramework::getSingletonPtr()->m_pMouse);
}

//=============================
// UPDATE CYCLE
//=============================

// Update the whole state.
void GameState::update(double timeSinceLastFrame)
{
	const float FPS_LIMIT = 1.0f/60.0f;

	// Update the GUI.
	m_FrameEvent.timeSinceLastFrame = timeSinceLastFrame;
	OgreFramework::getSingletonPtr()->m_pTrayMgr->frameRenderingQueued(m_FrameEvent);

	// Update the Newton physics world.
	m_pPhysWorld->update(timeSinceLastFrame);

	// Update all the ships in the scene using the ShipManager.
	m_ShipManager.updateShipManager(timeSinceLastFrame);

	// Update the OrbitCamera location and rotation.
	m_OrbitCamera.updateOrbitCamera(timeSinceLastFrame);

	// Leave and remove from the active state stack if requested.
	if (m_bQuit == true)
	{
		popAppState();
		return;
	}
}

//=============================
// INPUT
//=============================

// By default, all these functions should pass the input events back to the Framework.
bool GameState::keyPressed(const OIS::KeyEvent& keyEventRef)
{
	// If the user presses ESCAPE, then start the shutdown process.
	if (OgreFramework::getSingletonPtr()->m_pKeyboard->isKeyDown(OIS::KC_ESCAPE))
	{
		m_bQuit = true;
		return true;
	}

	// Pass on the player input to the ShipInput class.
	m_pPlayerInput->injectKeyPressed(keyEventRef);

	OgreFramework::getSingletonPtr()->keyPressed(keyEventRef);
	return true;
}

// By default, all these functions should pass the input events back to the Framework.
bool GameState::keyReleased(const OIS::KeyEvent &keyEventRef)
{
	// Pass on the player input to the ShipInput class.
	m_pPlayerInput->injectKeyReleased(keyEventRef);

    OgreFramework::getSingletonPtr()->keyPressed(keyEventRef);
    return true;
}

// By default, all these functions should pass the input events back to the Framework.
bool GameState::mouseMoved(const OIS::MouseEvent &evt)
{
	// Pass the movement back into the OgreFramework.
	OgreFramework::getSingletonPtr()->mouseMoved(evt);
    return true;
}

// By default, all these functions should pass the input events back to the Framework.
bool GameState::mousePressed(const OIS::MouseEvent &evt, OIS::MouseButtonID id)
{
	// Inject the mouse pressed keys to the OrbitCamera.
	m_OrbitCamera.injectMouseButtonDown(id);

	// Pass the movement back into the OgreFramework.
	OgreFramework::getSingletonPtr()->mouseMoved(evt);
    return true;
}

// By default, all these functions should pass the input events back to the Framework.
bool GameState::mouseReleased(const OIS::MouseEvent &evt, OIS::MouseButtonID id)
{
	// Inject the mouse released keys to the OrbitCamera.
	m_OrbitCamera.injectMouseButtonRelease(id);

	// Pass the movement back into the OgreFramework.
	OgreFramework::getSingletonPtr()->mouseMoved(evt);
    return true;
}

//=============================
// PAUSE
//=============================

// Called by the app state manager when the game state is to be paused.
bool GameState::pause()
{
    OgreFramework::getSingletonPtr()->m_pLog->logMessage("Pausing GameState..."); 
    return true;
}

//=============================
// RESUME
//=============================

// Called by the AppStateManager when the game state is to be resumed. Also sets
// the camera to actively use the Viewport.
void GameState::resume()
{
	OgreFramework::getSingletonPtr()->m_pLog->logMessage("Resuming GameState...");
	OgreFramework::getSingletonPtr()->m_pViewport->setCamera(m_pCamera);
	m_bQuit = false;
}


//=============================
// DESTRUCTOR
//=============================

// Similar to the pause function, but it also destroys the Camera, SceneManager
// and the RaySceneQuery.
void GameState::exit()
{
	// Clear the player input. Player ship deletes used to be handled here, but
	// it's now done by the ShipManager.
	delete m_pPlayerInput;

	// Clear the newton physics world.
	delete m_pPhysWorld;

	OgreFramework::getSingletonPtr()->m_pLog->logMessage("Leaving GameState...");

	m_pSceneMgr->destroyCamera(m_pCamera);

	if (m_pSceneMgr)
		OgreFramework::getSingletonPtr()->m_pRoot->destroySceneManager(m_pSceneMgr);
}